package com.hcc.gateway.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hcc.gateway.common.domain.DubboReq;
import com.hcc.gateway.common.utils.DubboUtils;
import com.hcc.gateway.dao.mapper.ApiConfigDao;
import com.hcc.gateway.domain.enums.ApiParamMappingTypeEnum;
import com.hcc.gateway.domain.enums.ApiStatusEnum;
import com.hcc.gateway.domain.enums.ProtocolEnum;
import com.hcc.gateway.domain.po.ApiConfigPo;
import com.hcc.gateway.domain.po.ApiParamMappingPo;
import com.hcc.gateway.domain.req.ApiConfigQueryReq;
import com.hcc.gateway.domain.resp.PageResult;
import com.hcc.gateway.domain.vo.ApiConfigFormVo;
import com.hcc.gateway.domain.vo.ApiConfigVo;
import com.hcc.gateway.domain.vo.ApiParamMappingVo;
import com.hcc.gateway.domain.vo.ParamTypeConfigVo;
import com.hcc.gateway.service.ApiConfigService;
import com.hcc.gateway.service.ApiParamMappingService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.stream.Collectors;

/**
 * ApiConfigServiceImpl
 *
 * @author shengjun.hu
 * @date 2021/12/29
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class ApiConfigServiceImpl extends ServiceImpl<ApiConfigDao, ApiConfigPo> implements ApiConfigService {

    @Autowired
    private ApiParamMappingService apiParamMappingService;

    @Override
    public void saveOrUpdateApiConfig(ApiConfigFormVo req) {
        Assert.isFalse(req == null, "入参req不能为空！");
        if (req.getId() != null) {
            ApiConfigPo dbApiConfigPo = this.getById(req.getId());
            Assert.isFalse(dbApiConfigPo == null, "当前API配置不存在，无法更新！");
            Assert.isFalse(ApiStatusEnum.ONLINE.name().equals(dbApiConfigPo.getStatus()), "当前API状态不允许更新！");
        }

        ApiConfigPo apiConfigPo = new ApiConfigPo();
        BeanUtils.copyProperties(req, apiConfigPo);
        if (req.getId() == null) {
            apiConfigPo.setStatus(ApiStatusEnum.DRAFT.name());
            apiConfigPo.setCreateTime(LocalDateTime.now());
            apiConfigPo.setDeleted(0);
        }
        apiConfigPo.setUpdateTime(LocalDateTime.now());

        String protocol = req.getProtocol();
        if (ProtocolEnum.DUBBO.name().equals(protocol)) {
            apiConfigPo.setRetryCount(req.getRetryCount());
            if (CollectionUtil.isNotEmpty(req.getParamTypeConfigs())) {
                apiConfigPo.setParamTypeConfig(JSON.toJSONString(req.getParamTypeConfigs()));
            }

            DubboReq dubboReq = new DubboReq();
            BeanUtils.copyProperties(req, dubboReq);
            dubboReq.setRetries(req.getRetryCount());

            apiConfigPo.setTargetUrl(DubboUtils.mergeUrl(dubboReq));
        }

        if (req.getId() == null) {
            this.save(apiConfigPo);
        } else {
            this.updateById(apiConfigPo);
            LambdaQueryWrapper<ApiParamMappingPo> deleteQueryWrapper = Wrappers.<ApiParamMappingPo>lambdaQuery()
                    .eq(ApiParamMappingPo::getApiConfig, apiConfigPo.getId());
            apiParamMappingService.remove(deleteQueryWrapper);
        }
        ApiParamMappingVo reqParamMapping = req.getReqParamMapping();
        if (reqParamMapping != null) {
            reqParamMapping.setApiConfig(apiConfigPo.getId());
            reqParamMapping.setType(ApiParamMappingTypeEnum.REQ.name());
            apiParamMappingService.saveApiParamMapping(reqParamMapping);
        }
        ApiParamMappingVo respParamMapping = req.getRespParamMapping();
        if (respParamMapping != null) {
            respParamMapping.setApiConfig(apiConfigPo.getId());
            respParamMapping.setType(ApiParamMappingTypeEnum.RESP.name());
            apiParamMappingService.saveApiParamMapping(respParamMapping);
        }
    }

    @Override
    public PageResult<ApiConfigVo> findApiConfigByPage(ApiConfigQueryReq req) {
        Page<ApiConfigPo> apiConfigPage = new Page<>();
        apiConfigPage.setCurrent(req.getCurrentPage());
        apiConfigPage.setSize(req.getPageSize());

        Page<ApiConfigPo> page = this.lambdaQuery()
                .like(StrUtil.isNotEmpty(req.getApiName()), ApiConfigPo::getApiName, req.getApiName())
                .like(StrUtil.isNotEmpty(req.getPath()), ApiConfigPo::getPath, req.getPath())
                .eq(StrUtil.isNotEmpty(req.getProtocol()), ApiConfigPo::getProtocol, req.getProtocol())
                .eq(StrUtil.isNotEmpty(req.getStatus()), ApiConfigPo::getStatus, req.getStatus())
                .orderByDesc(ApiConfigPo::getCreateTime)
                .page(apiConfigPage);

        PageResult<ApiConfigVo> pageResult = new PageResult<>();
        pageResult.setCurrentPage(req.getCurrentPage());
        pageResult.setPageSize(req.getPageSize());
        pageResult.setTotalCount(0L);
        pageResult.setPageCount(0L);
        pageResult.setResult(Collections.emptyList());
        if (page != null) {
            pageResult.setCurrentPage(pageResult.getCurrentPage());
            pageResult.setPageSize(page.getSize());
            pageResult.setTotalCount(page.getTotal());
            pageResult.setPageCount(page.getPages());
            if (CollectionUtil.isNotEmpty(page.getRecords())) {
                pageResult.setResult(
                        page.getRecords().stream()
                                .map(p -> {
                                    ApiConfigVo apiConfigVo = new ApiConfigVo();
                                    BeanUtils.copyProperties(p, apiConfigVo);

                                    return apiConfigVo;
                                })
                        .collect(Collectors.toList())
                );
            }
        }

        return pageResult;
    }

    @Override
    public void updateStatus(Long id, ApiStatusEnum statusEnum) {
        ApiConfigPo apiConfigPo = this.getById(id);
        Assert.isFalse(apiConfigPo == null, "当前API配置不存在！");
        if (ApiStatusEnum.ONLINE.equals(statusEnum)) {
            Assert.isTrue(ApiStatusEnum.DRAFT.name().equals(apiConfigPo.getStatus())
                    || ApiStatusEnum.OFFLINE.name().equals(apiConfigPo.getStatus()), "当前状态不允许上线");
        }
        if (ApiStatusEnum.OFFLINE.equals(statusEnum)) {
            Assert.isTrue(ApiStatusEnum.ONLINE.name().equals(apiConfigPo.getStatus()), "当前状态不允许下线");
        }

        this.lambdaUpdate()
                .set(ApiConfigPo::getStatus, statusEnum.name())
                .eq(ApiConfigPo::getId, id)
                .update();
    }

    @Override
    public ApiConfigFormVo getApiConfigById(Long id) {
        ApiConfigPo apiConfigPo = this.getById(id);
        if (apiConfigPo == null) {
            return null;
        }
        ApiConfigFormVo apiConfigFormVo = new ApiConfigFormVo();
        BeanUtils.copyProperties(apiConfigPo, apiConfigFormVo);
        apiConfigFormVo.setParamTypeConfigs(Collections.emptyList());
        if (ProtocolEnum.DUBBO.name().equals(apiConfigPo.getProtocol())) {
            DubboReq dubboReq = DubboUtils.parseUrl(apiConfigPo.getTargetUrl());
            BeanUtils.copyProperties(dubboReq, apiConfigFormVo);
        }
        if (StrUtil.isNotEmpty(apiConfigPo.getParamTypeConfig())) {
            apiConfigFormVo.setParamTypeConfigs(JSON.parseArray(apiConfigPo.getParamTypeConfig(), ParamTypeConfigVo.class));
        }
        ApiParamMappingVo reqParamMapping = apiParamMappingService.findApiParamMappingByApiConfig(id, ApiParamMappingTypeEnum.REQ);
        apiConfigFormVo.setReqParamMapping(reqParamMapping);

        ApiParamMappingVo respParamMapping = apiParamMappingService.findApiParamMappingByApiConfig(id, ApiParamMappingTypeEnum.RESP);
        apiConfigFormVo.setRespParamMapping(respParamMapping);

        return apiConfigFormVo;
    }

}
